﻿using DotNetOpenAuth.AspNet;
using DotNetOpenAuth.AspNet.Clients;
using DotNetOpenAuth.OpenId.Provider;
using Inovout.Account.Web.Models;
using Inovout.Account.Web.OpenIdClients;
using Inovout.Cloud.Identity;
using Inovout.EntityPropertyValidation;
using Inovout.Memberships;
using Inovout.Memberships.Models;
using Inovout.Memberships.Services;
using Inovout.Organizations.Memberships.Services;
using Inovout.People.Memberships.Models;
using Inovout.People.Memberships.Services;
using Inovout.RedictUrls;
using Inovout.Security;
using Inovout.Services;
using Inovout.Web.Mvc;
using Microsoft.Web.WebPages.OAuth;
using System;
using System.Collections.Generic;
using System.Security.Policy;
using System.Threading;
using System.Web.Mvc;
using System.Web.Security;

namespace Inovout.Account.Web.Controllers
{
    [AllowAnonymous]
    public class AccountController : BaseController
    {
        private IFormsAuthenticationService authenticateService;
        private IOrganizationMembershipService organizationMembershipService;
        private IMembershipUserService membershipUserService;

        public AccountController(IFormsAuthenticationService authenticateService,
            IOrganizationMembershipService organizationMembershipService,
            IMembershipUserService membershipUserService)
        {
            this.organizationMembershipService = organizationMembershipService;
            this.authenticateService = authenticateService;
            this.membershipUserService = membershipUserService;
        }

        protected ActionResult SignInSuccess(IMembershipUser membershipUser, bool isFormsUserCredential, bool rememberMe, string returnUrl)
        {
            Inovout.Web.Security.FormsAuthenticationService formsAuthenticationService = new Inovout.Web.Security.FormsAuthenticationService();
            formsAuthenticationService.SignIn(membershipUser.Name, isFormsUserCredential.ToString(), rememberMe);

             // FormsAuthentication.SetAuthCookie(membershipUser.Name, rememberMe);
            if (!string.IsNullOrEmpty(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return Redirect("~/");
            }
        }

        public ActionResult SignUp(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }

        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult SignUp(OrganizationSignUpInfo model, string returnUrl)
        {
            ValidateIsFormsUserOrNot(model);

            if (ModelState.IsValid)
            {
                var memberUser = (MembershipUser)membershipUserService.FindByByUserNameOrEmailForLocal(model.UserName,model.OrganizationEmail);
                if (memberUser != null)
                {
                    ModelState.AddModelError("OrganizationEmail", "该邮箱已存在");
                    return View(model);
                }

                //注册逻辑，并注意跳转至首页，建议跳转至注册成功页面。
                var status = MembershipUserStatus.Inactivate;
                MembershipUser membershipUser = (MembershipUser)organizationMembershipService.Create(model.Organization, model.FormsUserCredential, status);
                var membership = (Inovout.Memberships.Models.Membership)membershipUser.Membership;
                CreateValidationResult(membership.Id);
                RedirctUrl entity = CreateRedirctUrl(model.OrganizationEmail, membershipUser.Id, returnUrl);
                string result = SendEmail(model.OrganizationEmail, entity.ActionUrl);

                if (!string.IsNullOrEmpty(result))
                {//如果该邮箱地址是不可用的，显示错误消息
                    ModelState.AddModelError("UserName", result);
                }
                else
                {
                    return RedirectToAction("Activate", "MembershipUser", new { email = model.OrganizationEmail, membershipUser = membershipUser.Id, returnUrl = returnUrl });
                }
            }
            // 如果我们进行到这一步时某个地方出错，则重新显示表单
            return View(model);
        }

        private static string SendEmail(string email, string actionUrl)
        {

            string subject = "3Cloud 新用户激活邮件";
            string plate = @"亲爱的$(email)："
                + Environment.NewLine
                + Environment.NewLine + "感谢您申请注册三宝云！请点击链接激活账户："
                + Environment.NewLine + "$(url)"
                + Environment.NewLine
                + Environment.NewLine + "如果上述链接点击无效，请将该网页地址复制到浏览器地址栏中打开。"
                + Environment.NewLine
                + Environment.NewLine + "如果您没有申请注册三宝云，请忽略此邮件。"
                + Environment.NewLine
                + Environment.NewLine + "---------------------------------------------------------------------------------------------------------"
                + Environment.NewLine
                + Environment.NewLine + "本邮件由系统自动发出，请勿回复。";

            Dictionary<string, string> contents = new Dictionary<string, string>();
            contents.Add("email", email);
            contents.Add("url", actionUrl);
            string result = Inovout.ComponentRegistry.Resolve<IRedictUrlService>().SendMail(plate, contents, subject);

            return result;
        }

        private RedirctUrl CreateRedirctUrl(string eamil, int id, string returnUrl)
        {

            RedirctUrl entity = new RedirctUrl();
            //if (returnUrl.ToLower().IndexOf("me") != -1)
            //{
            //    entity.OrginalUrl = (new Uri(this.Request.Url, Url.Action("ActivateUser", "MembershipUser", new { Email = eamil }))).AbsoluteUri;
            //}
            //else
            //{
            entity.OrginalUrl = (new Uri(this.Request.Url, Url.Action("ActivateUser", "MembershipUser", new { Email = eamil, MembershipUser = id, ReturnUrl = returnUrl }))).AbsoluteUri;
            //}
            entity.FailedUrl = (new Uri(this.Request.Url, Url.Action("ActivateFailed", "MembershipUser", new { Email = eamil }))).AbsoluteUri;
            string guid = Guid.NewGuid().ToString("N");
            entity.ActionUrl = (new Uri(this.Request.Url, Url.Action("Index", "Go", new { key = guid }))).AbsoluteUri;
            Inovout.ComponentRegistry.Resolve<IRedictUrlService>().Save(entity);
            return entity;
        }

        private void ValidateIsFormsUserOrNot(OrganizationSignUpInfo model)
        {
            if (model.IsExtensional)
            {
                ModelState.Remove("UserName");
                ModelState.Remove("Password");
                ModelState.Remove("ConfirmPassword");
            }
            else
            {
                ModelState.Remove("OpenIdClaimedIdentifier");
                ModelState.Remove("OpenIdProvider");
                ModelState.Remove("OpenIdUserName");
            }
        }

        private static void CreateValidationResult(int id, bool isValid = false)
        {
            Inovout.Memberships.Models.Membership membership = Inovout.ComponentRegistry.Resolve<MembershipService>().FindById(id);
            EntityPropertyValidationResult validationResult = new EntityPropertyValidationResult();
            validationResult.EntityType = membership.MembershipType.ToString();
            validationResult.EntityId = membership.Id.ToString();
            validationResult.IsValid = isValid;
            ComponentRegistry.Resolve<IEntityPropertyValidationResultService>().SaveValidationResult(validationResult, "Email");
        }

        public ActionResult SignOut(string returnUrl)
        {
            FormsAuthentication.SignOut();
            if (string.IsNullOrEmpty(returnUrl))
            {
                return RedirectToAction("SignIn");
            }
            else
            {
                return Redirect(returnUrl);
            }
        }

        public ActionResult Login(string provider, string returnUrl, bool? skipBind)
        {
            return SignIn(provider, returnUrl, skipBind);
        }

        public ActionResult SignIn(string provider, string returnUrl, bool? skipBind)
        {
            ViewBag.ReturnUrl = returnUrl;
            if (!string.IsNullOrEmpty(provider))
            {
                return ExtensionalSignIn(provider, returnUrl, skipBind);
            }
            return View("SignIn");
            //自定义登录页
            //var request = ProviderEndpoint.PendingRequest;
            //IdentirySettings identitySettings = null;
            //if (request != null)
            //{
            //    identitySettings = ComponentRegistry.Resolve<SettingsService>().Get(request.Realm.ToString());
            //}
            //if (identitySettings == null)
            //{
            //    return View("SignIn");
            //}
            //else
            //{
            //    ViewBag.Title = "登录" + identitySettings.TitleItem.Value;
            //    ViewBag.IdentitySettings = identitySettings;
            //    //Regex.Replace(inputString, "<img.*?>", string.Empty)
            //    return View("IdentitrySignIn");
            //}
        }

        [HttpPost]
        //[ValidateAntiForgeryToken]
        public ActionResult SignIn(SignInInfo signInModel, string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            if (ModelState.IsValid)
            {
                MembershipUser membershipUser = (MembershipUser)membershipUserService.FindByUserNameOrEmail(signInModel.UserName);

                if (membershipUser != null && authenticateService.Authenticate(signInModel.UserName, signInModel.Password))
                {
                    if (membershipUser.Status == MembershipUserStatus.Inactivate)
                    {
                        return RedirectToAction("Activate", "MembershipUser", new { email = membershipUser.Email, membershipUser = membershipUser.Id, message = "您的邮箱已被注册，但尚未激活。" });
                    }
                    if (membershipUser.Status == MembershipUserStatus.Locking)
                    {
                        return RedirectToAction("Message", new { messageType = MessageType.Warning, message = "此账户已锁定，请联系管理员。" });
                    }
                    return SignInSuccess(membershipUser, true, signInModel.RememberMe, returnUrl);
                }

                ModelState.AddModelError("", "提供的用户名或密码不正确");
            }
            return View(signInModel);
        }

        [AllowAnonymous]
        ////[ValidateAntiForgeryToken]
        public ActionResult ExtensionalSignIn(string provider, string returnUrl, bool? skipBind)
        {
            if (skipBind.HasValue && skipBind.Value)
            {
                return new AccountAuthenticationResult(provider, Url.Action("ExtensionalSignInCallback", new { ReturnUrl = returnUrl, skipBind = skipBind }));
            }
            else
            {
                return new AccountAuthenticationResult(provider, Url.Action("ExtensionalSignInCallback", new { ReturnUrl = returnUrl }));
            }
        }

        [AllowAnonymous]
        [ChildActionOnly]
        public ActionResult ExtensionalSignInList(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return PartialView("_ExtensionalSignInList", OAuthWebSecurity.RegisteredClientData);
        }

        [AllowAnonymous]
        public ActionResult ExtensionalSignInCallback(string returnUrl, bool? skipBind)
        {
            AuthenticationResult authenticationResult = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExtensionalSignInCallback", new { ReturnUrl = returnUrl }));
            //获取认证服务结果不成功时，调用ExternalLoginFailure() action
            if (authenticationResult.IsSuccessful)
            {
                // GetAccessToken(GetExtraUserdata(authenticationResult.ExtraData, "accesstoken"));
                var clientData = OAuthWebSecurity.GetOAuthClientData(authenticationResult.Provider);
                var membershipUser = ComponentRegistry.Resolve<IMembershipUserService>().FindByProviderIdentifier(authenticationResult.Provider, authenticationResult.ProviderUserId);

                PersonSignUpInfo personSignUpInfo = new PersonSignUpInfo();
                if (clientData.AuthenticationClient is OAuth2Client)
                {
                    var accessTokenInfo = (clientData.AuthenticationClient as Inovout.Web.Security.BaseOAuth2Client).GetAndClearAccessToken(GetExtraUserdata(authenticationResult.ExtraData, "accesstoken"));
                    personSignUpInfo = AssginPersonSignUpInfo(authenticationResult, accessTokenInfo);
                }
                if (clientData.AuthenticationClient is OpenIdClient)
                {
                    personSignUpInfo = AssginPersonSignUpInfo(authenticationResult, null);
                }

                if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
                {
                    if (membershipUser != null)
                    {
                        return RedirectToAction("Message", new { messageType = MessageType.Failed, message = Url.Encode("此账户已存在，关联失败。 <a href='" + returnUrl + "'>继续</a>") });
                    }

                    Inovout.Memberships.Models.Membership membership = base.Membership as Memberships.Models.Membership;
                    var mainMembershipUser = ComponentRegistry.Resolve<IMembershipUserService>().FindByMembership(membership);

                    if (clientData.AuthenticationClient is OpenIdClient)
                    {
                        SaveNewOpenIdUserCredential(personSignUpInfo, mainMembershipUser);
                    }
                    if (clientData.AuthenticationClient is OAuth2Client)
                    {
                        SaveNewOAuthUserCredential(personSignUpInfo, mainMembershipUser);
                    }
                    // FormsAuthentication.SetAuthCookie(mainMembershipUser.Name, false);
                    return Redirect(returnUrl);
                    //return RedirectToAction("Link", "SystemUser", new { returnUrl = returnUrl });

                }
                if (membershipUser == null)
                {
                    if (clientData.AuthenticationClient is OpenIdClient)
                    {
                        membershipUser = ComponentRegistry.Resolve<IPersonMembershipService>().Create(personSignUpInfo.Person, personSignUpInfo.OpenIdUserCredential, MembershipUserStatus.Initial);
                    }
                    if (clientData.AuthenticationClient is OAuth2Client)
                    {
                        membershipUser = ComponentRegistry.Resolve<IPersonMembershipService>().Create(personSignUpInfo.Person, personSignUpInfo.OAuthUserCredential, MembershipUserStatus.Initial);
                    }
                }
                else if (membershipUser.Status == MembershipUserStatus.Initial)
                {
                    int memberId = ((Inovout.Memberships.Models.Membership)membershipUser.Membership).Id;
                    PersonMembership personMembership = ComponentRegistry.Resolve<IRepositoryService<PersonMembership>>().FindById(memberId);
                    //personMembership.Person = personSignUpInfo.Person;
                    personMembership.Person.Email = personSignUpInfo.PersonEmail;
                    personMembership.Person.Name = personSignUpInfo.PersonName;
                    personMembership.Person.Mobile = personSignUpInfo.PersonMobile;
                    ComponentRegistry.Resolve<IRepositoryService<PersonMembership>>().Save(personMembership);
                }
                if (skipBind.HasValue && skipBind.Value)
                {
                    return SignInSuccess(membershipUser, false, false, returnUrl);
                }
                // if (personSignUpInfo.PersonEmail!=null&&personSignUpInfo.PersonEmail.EndsWith("@samples.cn") && membershipUser.Status == MembershipUserStatus.Initial)
                if (personSignUpInfo.OpenIdProvider.Equals(MailAccountClient.ProviderName) && membershipUser.Status == MembershipUserStatus.Initial)
                {
                    CreateValidationResult(((Inovout.Memberships.Models.Membership)membershipUser.Membership).Id, true);
                    membershipUser.Status = MembershipUserStatus.Activate;
                    ComponentRegistry.Resolve<IRepositoryService<MembershipUser>>().Save((MembershipUser)membershipUser);
                }
                if (membershipUser.Status == MembershipUserStatus.Initial)
                {
                    return RedirectToAction("Bind", "Person", new { returnUrl = returnUrl, id = ((MembershipUser)membershipUser).Id, authenticationResult.Provider, authenticationResult.ProviderUserId });
                }
                if (membershipUser.Status == MembershipUserStatus.Inactivate)
                {
                    return RedirectToAction("Activate", "MembershipUser", new { email = membershipUser.Email, membershipUser = ((MembershipUser)membershipUser).Id, message = "您的邮箱已被注册，但尚未激活。" });
                }
                if (membershipUser.Status == MembershipUserStatus.Locking)
                {
                    return RedirectToAction("Message", new { messageType = MessageType.Warning, message = "此账户已锁定，请联系管理员。" });
                }
                return SignInSuccess(membershipUser, false, false, returnUrl);
            }
            return RedirectToAction("ExternalLoginFailure");
        }

        private static void SaveNewOAuthUserCredential(PersonSignUpInfo personSignUpInfo, IMembershipUser mainMembershipUser)
        {
            OAuthUserCredential oAuthUserCredential = new OAuthUserCredential
            {
                Email = personSignUpInfo.PersonEmail,
                AccountName = personSignUpInfo.PersonName,
                Provider = personSignUpInfo.OpenIdProvider,
                Identifier = personSignUpInfo.OpenIdClaimedIdentifier,
                Code = personSignUpInfo.Code,
                AccessToken = personSignUpInfo.AccessToken,
                AccessTokenExpiredTime = personSignUpInfo.AccessTokenExpiredTime,
                RefreshToken = personSignUpInfo.RefreshToken,
                RefreshTokenExpiredTime = personSignUpInfo.RefreshTokenExpiredTime
            };
            oAuthUserCredential.SystemUser = (SystemUser)mainMembershipUser.SystemUser;
            ComponentRegistry.Resolve<IRepositoryService<OAuthUserCredential>>().Save(oAuthUserCredential);
        }

        private static void SaveNewOpenIdUserCredential(PersonSignUpInfo personSignUpInfo, IMembershipUser mainMembershipUser)
        {
            OpenIdUserCredential openIdUserCredential = new OpenIdUserCredential
            {
                Email = personSignUpInfo.PersonEmail,
                AccountName = personSignUpInfo.PersonName,
                Provider = personSignUpInfo.OpenIdProvider,
                Identifier = personSignUpInfo.OpenIdClaimedIdentifier
            };
            openIdUserCredential.SystemUser = (SystemUser)mainMembershipUser.SystemUser;
            ComponentRegistry.Resolve<IRepositoryService<OpenIdUserCredential>>().Save(openIdUserCredential);
        }

        private static string GetExtraUserdata(IDictionary<string, string> dic, string key)
        {
            if (dic.ContainsKey(key))
            {
                return dic[key].Trim();
            }
            return null;
        }

        private static PersonSignUpInfo AssginPersonSignUpInfo(AuthenticationResult authenticationResult, Inovout.Web.Security.AccessTokenInfo accessTokenInfo)
        {
            var personSignUpInfo = new PersonSignUpInfo
            {
                OpenIdClaimedIdentifier = authenticationResult.ProviderUserId.Trim(),
                OpenIdProvider = authenticationResult.Provider.Trim(),
                OpenIdUserName = authenticationResult.ProviderUserId.Trim(),
                IsExtensional = true
            };
            personSignUpInfo.PersonName = GetExtraUserdata(authenticationResult.ExtraData, "username");
            personSignUpInfo.PersonEmail = GetExtraUserdata(authenticationResult.ExtraData, "email");
            personSignUpInfo.PersonMobile = GetExtraUserdata(authenticationResult.ExtraData, "mobile");
            if (accessTokenInfo != null)
            {
                personSignUpInfo.Code = accessTokenInfo.Code.Trim();
                personSignUpInfo.AccessToken = accessTokenInfo.AccessToken.Trim();
                personSignUpInfo.AccessTokenExpiredTime = Convert.ToInt64(accessTokenInfo.AccessTokenExpiredTime);
                personSignUpInfo.RefreshToken = accessTokenInfo.RefreshToken;
                personSignUpInfo.RefreshTokenExpiredTime = Convert.ToInt64(accessTokenInfo.RefreshTokenExpiredTime);
            }
            return personSignUpInfo;
        }



        [AllowAnonymous]
        public ActionResult ExternalLoginFailure()
        {
            return View();
        }

    }
}
